#pragma once

#include <iostream>
#include <queue>
#include <vector>
#include "Thread.hpp"
#include "Cond.hpp"
#include <cstdio>
// 单例线程池-懒汉模式
const static int defaultthreadnum = 3;

template <class T>
class ThreadPool
{
private:
    bool QueueIsEmpty()
    {
        return _q.empty();
    }
    void Rountine(const std::string &name)
    {
        // for test
        while (1)
        {
            // std::cout<<1<<std::endl;
            //  把任务从线程获取到线程私有：临界区->私有的栈
            T t;
            // 检测任务队列，获取任务，处理任务
            {
                LockGuard LockGuard(&_lock);
                // 判断是否有任务
                // 伪唤醒
                while (QueueIsEmpty() && _is_running) // 如果不运行了那就别休眠了
                {
                    // std::cout<<1<<std::endl;
                    _wait_thread_num++;
                    // 等条件变量
                    _cond.Wait(_lock);
                    // std::cout<<2<<std::endl;
                    _wait_thread_num--;
                }
                if (!_is_running && QueueIsEmpty())
                {
                    LOG(Loglevel::INFO) << name << "线程池要退出，并且任务队列为空，" << name << "退出";
                    break;
                }
                // 队列中一定有任务了,但是线程池可能退出
                // 如果线程池退出了，那就是处理残留任务，如果线程池没退出，那就是正常工作
                t = _q.front();
                _q.pop();
            }
            // std::cout<<2<<std::endl;
            t(); // 处理任务不需要在临界区内部

            LOG(Loglevel::DEBUG) << name << "handlder task:" << t.ResulttoString();
        }
    }

    ThreadPool(int threadnum = defaultthreadnum)
        : _is_running(false), _threadnum(threadnum), _wait_thread_num(0)
    {
        for (int i = 0; i < _threadnum; i++)
        {
            // Thread t(hello); 直接传肯定编译不过，因为hello里面有this指针
            // auto f=std::bind(hello,this);
            // Thread t(f);
            std::string name = "thread-" + std::to_string(i + 1);
            // Thread t([this](){
            //     this->hello();
            // },name);

            // _threads.push_back(std::move(t));

            _threads.emplace_back([this](const std::string &name)
                                  { this->Rountine(name); }, name);
        }
        LOG(Loglevel::INFO) << " thread pool obj create success";
    }
    ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;
    ThreadPool(const ThreadPool<T> &t) = delete; // 禁用这两个方法，让他不能直接构造对象
public:
    void Start()
    {
        if (_is_running)
            return;
        _is_running = true;

        // 不start这个线程压根就每创建
        for (auto &t : _threads)
        {
            t.Start();
        }
        LOG(Loglevel::INFO) << " thread pool running success";
    }

    // 线程退出的核心思想：让线程走正常的唤醒逻辑退出
    // 线程池要退出时：
    // 1、如果线程被唤醒&&任务队列没有任务 那就让线程退出
    // 2、如果线程被唤醒&&任务队列有任务，那就让线程执行完任务再退出
    // 3、线程本身没有被休眠，就让他把任务处理完，再退出。
    // 如果任务队列有任务，线程是不会休眠的
    void Stop()
    {
        if (!_is_running)
            return;

        _is_running = false; // 线程池别运行了

        if (_wait_thread_num)
        {
            _cond.NotifyAll();
        }
        // if (!_is_running)
        //     return;
        // _is_running = false;
        // for (auto &t : _threads)
        // {
        //     t.Stop();
        // }
    }
    void Wait()
    {
        for (auto &t : _threads)
        {
            t.Join();
        }
        LOG(Loglevel::INFO) << "thread pool wait success";
    }
    void Enqueue(const T &t)
    {
        if (!_is_running) // 如果线程池要退出了就直接返回
            return;
        // 任务入队
        {
            LockGuard lockguard(&_lock);
            _q.push(t);
            if (_wait_thread_num > 0)
                _cond.NotifyOne();
        }
    }
    static std::string ToHex(ThreadPool<T> *addr)
    {
        char buffer[64];
        snprintf(buffer, sizeof(buffer), "%p", addr);
        return buffer;
    }
    // 成员函数只能通过对象的方式调用，但是我们是想通过调用这个方法来创建出对象
    // 所以说我们要把这个函数搞成静态的
    // 然而，我们必须得把他放在类内，因为这样才能访问他的私有的构造函数和变量。
    // 不在类内的话，还有去写一堆的暴露内部属性的接口
    static ThreadPool<T> *GetInstance()
    {
        // 获取单例
        // 加锁保护,防止多个线程都进入了if循环，都去实例化这个线程池
        {
            //双重保护，在保证安全的前提下，提高效率
            if (!_instance)
            {
                LockGuard Lockguard(&_singleton_lock);
                if (!_instance)
                {
                    // 静态成员函数调用其他成员函数也必须通过对象.的方式来调用，所以我们把ToHex弄成静态的
                    LOG(Loglevel::DEBUG) << "线程池单例首次被使用,创建并初始化,addr:" << ToHex(_instance);
                    _instance = new ThreadPool<T>();
                    _instance->Start();
                }
            }
        }
        // else
        // {
        //     LOG(Loglevel::DEBUG)<<"线程池单例已经存在，直接获取,addr:"<<ToHex(_instance);
        // }
        return _instance;
    }
    ~ThreadPool()
    {
    }

private:
    // 任务队列
    std::queue<T> _q; // 整体使用7的临界资源

    // 多个线程
    std::vector<Thread> _threads; // 线程池
    // 1、创建线程对象  2、让线程对象启动
    int _threadnum;
    int _wait_thread_num;

    // 保护机制
    Mutex _lock;
    Cond _cond;

    // 其他属性
    bool _is_running; // 线程池是否是启动的

    // 单例中静态指针
     static ThreadPool<T> *_instance;
     //使用volatile可以防止编译器对变量进行激进的优化，确保每次访问都从内存中读取，而不是使用寄存器中的缓存值。
     //这个地方加了其他地方就都得加
    static Mutex _singleton_lock; // 保护单例模式
};

template <class T>
ThreadPool<T> *ThreadPool<T>::_instance = nullptr;

template <class T>
Mutex ThreadPool<T>::_singleton_lock;